package com.edu.compile;

import com.edu.compile.check.InspectFunJs;
import com.edu.compile.constant.EventEnum;
import com.edu.compile.constant.NodeTypeEnum;
import com.edu.compile.exception.InspectException;
import com.edu.compile.template.NodeTemplateParse;
import com.edu.compile.template.TemplatePolymerization;
import com.edu.compile.template.ftl.block.Http;
import com.edu.compile.template.ftl.block.IfElse;
import com.edu.compile.template.ftl.block.Loop;
import com.edu.compile.template.ftl.block.LoopEnd;
import com.edu.compile.template.ftl.block.Switch;
import com.edu.compile.template.ftl.call.NodeCall;
import com.edu.compile.template.ftl.node.Calc;
import com.edu.compile.template.ftl.node.HttpResponse;
import com.edu.compile.template.ftl.node.Redis;
import com.edu.compile.template.ftl.node.TripartiteApi;
import com.edu.compile.util.CompilerDispatcher;
import com.edu.model.Response;
import com.edu.model.constant.FileConfig;
import com.edu.model.dto.NodeMetadata;
import com.edu.util.ExceptionPointer;
import com.edu.util.file.DefaultFileStorage;
import com.edu.util.file.FileUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author Yang
 * @Description
 * @Date 11:23 2021/5/7
 **/
public class CompileContainer {

    private static final Logger LOGGER = LoggerFactory.getLogger(CompileContainer.class);

    private static Map<String, Class<? extends NodeTemplateParse>> cacheMap = new HashMap<>();

    static {
        cacheMap.put(NodeTypeEnum.HTTP.getName(), Http.class);
        cacheMap.put(NodeTypeEnum.IF_ELSE.getName(), IfElse.class);
        cacheMap.put(NodeTypeEnum.SWITCH.getName(), Switch.class);
        cacheMap.put(NodeTypeEnum.CALC.getName(), Calc.class);
        cacheMap.put(NodeTypeEnum.NODE.getName(), NodeCall.class);
        cacheMap.put(NodeTypeEnum.LOOP.getName(), Loop.class);
        cacheMap.put(NodeTypeEnum.LOOP_END.getName(), LoopEnd.class);
        cacheMap.put(NodeTypeEnum.TRIPARTITE_API.getName(), TripartiteApi.class);
        cacheMap.put(NodeTypeEnum.REDIS.getName(), Redis.class);
        cacheMap.put(NodeTypeEnum.HTTP_RESPONSE.getName(), HttpResponse.class);
    }

    /**
     * id=node
     */
    private Map<String, NodeMetadata> id_node = new HashMap<>();

    /**
     * id=template
     */
    private Map<String, NodeTemplateParse> id_template = new HashMap<>();

    /**
     * id=pid
     */
    private Map<String, String> id_pid = new HashMap<>();

    /**
     * id=直接子节点
     */
    private Map<String, List<NodeMetadata>> id_subNode = new HashMap<>();

    private TemplatePolymerization templatePolymerization = new TemplatePolymerization();

    private NodeMetadata root = null;

//    public void init(String eventId, List<NodeMetadata> nodeMetadataList, String lang) {
//        this.lang = lang;
//        templatePolymerization.setEventId(eventId);
//        for (NodeMetadata nodeMetadata : nodeMetadataList) {
//            Class<? extends NodeTemplateModel> clazz = cacheMap.get(nodeMetadata.getNodeType());
//            addNodeTemplateModel(nodeMetadata, clazz);
//            id_node.put(nodeMetadata.getNodeId(), nodeMetadata);
//            String pid = nodeMetadata.getParentNodeId();
//
//            id_pid.put(nodeMetadata.getNodeId(), pid);
//            if (StringUtils.isNotBlank(pid)) {
//                List<NodeMetadata> subNodes = id_subNode.getOrDefault(pid, new ArrayList<>());
//                if (subNodes.size() == 0) {
//                    id_subNode.put(nodeMetadata.getParentNodeId(), subNodes);
//                }
//                subNodes.add(nodeMetadata);
//            }
//        }
//    }

    public NodeMetadata getSubNode(String pid) {
        List<NodeMetadata> nodeMetadataList = id_subNode.get(pid);
        return nodeMetadataList == null || nodeMetadataList.size() == 0 ? null : nodeMetadataList.get(0);
    }

    public List<NodeMetadata> getSubNodes(String pid) {
        return id_subNode.get(pid);
    }

    public String findParentNodeId(NodeMetadata nodeMetadata) {
        String pid = id_pid.get(nodeMetadata.getNodeId());
        if (pid == null) {
            throw new IllegalArgumentException(nodeMetadata.getNodeName() + " not found pid");
        }
        return pid;
    }

//    public Response compile() {
//
//        NodeMetadata nodeMetadata = id_node.get(templatePolymerization.getNodeId());
//        if (nodeMetadata == null) {
//            return Response.fail("未找到事件节点");
//        }
//        EventNode firstNode = new EventNode();
//        firstNode.setEventId(templatePolymerization.getEventId());
//        firstNode.setConfig(nodeMetadata.getConfig());
//        firstNode.setNodeType(nodeMetadata.getNodeType());
//        firstNode.setParentNodeId(null);
//        firstNode.setNodeId(nodeMetadata.getNodeId());
//        firstNode.setNodeName(nodeMetadata.getNodeName());
//        Response compileResponse = Response.success();
//        AbstractCompileResult abstractCompileResult = null;
//        String beanName = nodeMetadata.getNodeType() + "_compiler";
//        AbstractCompilerParser abstractCompilerParser = CompilerDispatcher.getHandler(beanName);
//        if (abstractCompilerParser != null) {
//            abstractCompileResult = abstractCompilerParser.newCompileResult();
//            if (abstractCompileResult instanceof CompileResult) {
//                ((CompileResult) abstractCompileResult).setEventContainer(this);
//                ((CompileResult) abstractCompileResult).setTemplatePolymerization(templatePolymerization);
//            }
//            String applyName = "event-" + templatePolymerization.getEventId();
//            abstractCompileResult.setApplyName(applyName);
//            LOGGER.info("待发布事件对应的服务名称：" + applyName);
//            compileResponse.setMsg("待发布事件对应的服务名称：" + applyName);
//            try {
//                abstractCompilerParser.compile0(abstractCompileResult, abstractCompilerParser.parseArgs(firstNode), firstNode.getEventId());
//            } catch (Exception e) {
//                LOGGER.error("compile error: {}", e.getMessage());
//                return Response.fail(abstractCompileResult, "代码编译异常：" + e.getMessage());
//            }
//            Response response = storeCompileFile(abstractCompileResult, abstractCompilerParser);
//            if (response.isFail()) {
//                return response;
//            }
//        } else {
//            LOGGER.error("Unsupported nodeType {}", beanName);
//            return Response.fail("Unsupported nodeType: " + beanName);
//        }
//        LOGGER.info("storePath：{}", abstractCompileResult.getStorePath());
//        compileResponse.setData(abstractCompileResult);
//        return compileResponse;
//    }

    private Response storeCompileFile(AbstractCompileResult abstractCompileResult, AbstractCompilerParser abstractCompilerParser) {
        final FileUtil fileUtil = new FileUtil();
        try {
            fileUtil.createFile(FileConfig.DEFAULT_CREATE_PATH + templatePolymerization.getEventId(), abstractCompilerParser.getFilePrefix(), abstractCompilerParser.getFileSuffix(), abstractCompileResult.getMainBodyFilename());
            fileUtil.append(abstractCompileResult.parse(), abstractCompileResult.getScriptList());
            fileUtil.appendModuleFile(abstractCompileResult.getInnerScriptMap(), FileConfig.JS_SUFFIX);
            new InspectFunJs(fileUtil.getFiles(), templatePolymerization.getEventId()).inspect();
            fileUtil.toZip();
            String storePath = fileUtil.store(new DefaultFileStorage(templatePolymerization.getEventId()));
            abstractCompileResult.setStorePath(storePath);
        } catch (InspectException e) {
            LOGGER.error("函数验证失败：{}", e.getMessage());
            return Response.fail(abstractCompileResult, "函数验证失败：" + e.getMessage());
        } catch (Exception e) {
            ExceptionPointer.echo(e, "文件上传失败");
            return Response.fail(abstractCompileResult, "文件上传失败：" + e.getMessage());
        } finally {
        }
        return Response.success();
    }

    public Response compile() {
        if (root == null) {
            throw new IllegalArgumentException("not found any root node");
        }
        Response compileResponse = Response.success();
        AbstractCompileResult abstractCompileResult = null;
        String beanName = root.getNodeType() + "_compiler";
        AbstractCompilerParser abstractCompilerParser = CompilerDispatcher.getCompiler(root);
        if (abstractCompilerParser != null) {
            abstractCompileResult = abstractCompilerParser.newCompileResult();
            abstractCompileResult.setEventContainer(this);
            abstractCompileResult.setTemplatePolymerization(templatePolymerization);
            String applyName = "event-" + templatePolymerization.getEventId();
            abstractCompileResult.setApplyName(applyName);
            LOGGER.info("待发布事件对应的服务名称：" + applyName);
            compileResponse.setMsg("待发布事件对应的服务名称：" + applyName);
            try {
                abstractCompilerParser.compile0(abstractCompileResult, root);
            } catch (Exception e) {
                LOGGER.error("compile error: {}", e.getMessage());
                return Response.fail(abstractCompileResult, "代码编译异常：" + e.getMessage());
            }
            Response response = storeCompileFile(abstractCompileResult, abstractCompilerParser);
            if (response.isFail()) {
                return response;
            }
        } else {
            LOGGER.error("Unsupported nodeType {}", beanName);
            return Response.fail("Unsupported nodeType: " + beanName);
        }
        LOGGER.info("storePath：{}", abstractCompileResult.getStorePath());
        compileResponse.setData(abstractCompileResult);
        return compileResponse;
    }

    public void init(String eventId, List<NodeMetadata> nodeMetadataList) {
        init(eventId, nodeMetadataList, this.root);
    }

    public void init(String eventId, List<NodeMetadata> nodeMetadataList, NodeMetadata root) {
        templatePolymerization.setEventId(eventId);

        for (NodeMetadata nodeMetadata : nodeMetadataList) {
            confirmRoot(nodeMetadata, root);
            id_node.put(nodeMetadata.getNodeId(), nodeMetadata);
            String pid = nodeMetadata.getParentNodeId();
            id_pid.put(nodeMetadata.getNodeId(), pid);
            if (StringUtils.isNotBlank(pid)) {
                List<NodeMetadata> subNodes = id_subNode.getOrDefault(pid, new ArrayList<>());
                if (subNodes.size() == 0) {
                    id_subNode.put(nodeMetadata.getParentNodeId(), subNodes);
                }
                subNodes.add(nodeMetadata);
            }
        }
    }

    private void confirmRoot(NodeMetadata item, NodeMetadata root) {
        if (root != null) {
            this.root = root;
            templatePolymerization.setRequestEntry(this.root);
            return;
        }
        if (EventEnum.testEvent(item.getNodeType())) {
            this.root = item;
            templatePolymerization.setRequestEntry(this.root);
        }
    }

    private Response storeCompileFile(AbstractCompileResult abstractCompileResult) {
        abstractCompileResult.parse();
//        abstractCompileResult.getScriptList();
        return Response.success();
    }

}
